import Cesium, { Matrix4 } from '../Ces/Cesium'
import Util from '../Core/Util'
import WeDisplay from "./WeDisplay"
import WeGeometryUtil from './WeGeometryUtil'
import WeGridExtrusionShaders from './WeGridExtrusionShaders'
import WeUniform from './WeUniform'

class WeGridExtrusion {
  show: boolean = true
  translucent: boolean
  private display: WeDisplay
  private command: any
  private weUniform: WeUniform
  private modelMatrix: Matrix4
  constructor(options) {
    this.display = options.display
    this.translucent = options.translucent ?? false
    const buildQuad = options.buildQuad ?? false
    const buildCube = options.buildCube ?? false
    const buildFace = !buildCube && !buildQuad
    const numPointOfLine = buildQuad ? 2 : buildCube ? 10 : 1
    const numVertexPerLine = numPointOfLine * (buildFace ? 1 : 2)

    const rectangle = options.display.imageryLayer.rectangle
    const meterSize = Util.computeRectangleMeter(rectangle)
    const meterWidth = meterSize.width
    const meterHeight = meterSize.height
    const useRTCenter = Math.max(meterWidth, meterHeight) < 10000

    this.weUniform = new WeUniform({
      name: "WeGridExtrusionUniforms",
      floatUniform: {
        numLon: options.numLon ?? 64,
        numLat: options.numLat ?? 64,
        offsetZ: options.offsetZ ?? 0,
        cubeBaseHeight: options.cubeBaseHeight ?? 0,
        numPointOfLine,
        numVertexPerLine,
        extrusionScale: options.extrusionScale ?? 1,
        normalFactor: options.normalFactor ?? 1.0,
        alphaFactor: options.alphaFactor ?? 0.0,
        enableLight: (options.enableLight ?? true) ? 1.0 : 0.0,
        enableNormalMap: (options.enableNormalMap ?? false) ? 1.0 : 0.0,
        useRTCenter: useRTCenter ? 1.0 : 0.0,
        meterWidth,
        meterHeight,
      }
    })

    if (useRTCenter) {
      const position = new Cesium.Cartesian3()
      Cesium.Cartesian3.fromRadians(
        rectangle.west + rectangle.width * 0.5,
        rectangle.south + rectangle.height * 0.5,
        0.0, Cesium.Ellipsoid.default, position)
      this.modelMatrix = Util.getEnuModelMatrix(position, new Cesium.Matrix4())
    }
  }

  rebuild(context) {
    const { display, translucent } = this
    const { numLon, numLat, numPointOfLine, numVertexPerLine } = this.weUniform.Properties
    const renderState = Cesium.RenderState.fromCache({
      cull: {
        enabled: true,
        face: Cesium.CullFace.BACK,
      },
      depthTest: {
        enabled: true,
      },
      blending: translucent ? Cesium.BlendingState.ALPHA_BLEND : Cesium.BlendingState.DISABLE,
    })

    const { vertexArray, attributeLocations } = WeGeometryUtil.createQuadGeometry(
      context, numLon, numLat, numPointOfLine, numVertexPerLine
    )

    const uniformMap = {}
    display.setupUniformMap(uniformMap)
    this.weUniform.setupUniformMap(uniformMap)

    if (display.customTextureUniform0) {
      display.customTextureUniform0.setupUniformMap(uniformMap)
    }

    const baseSource = `
    ${display.getFragmentShaderSource(true, true, true)}
    ${display.customTextureUniform0 ? display.customTextureUniform0.getSource() : ''}
    `
    const vertexShaderSource = `
        ${this.weUniform.getSource(true)}
        ${baseSource}
        ${WeGridExtrusionShaders.vsSource}
        `

    const fragmentShaderSource = `
        ${this.weUniform.getSource(true)}
        ${baseSource}
        ${display.normalEffect.shaderSource}
        ${WeGridExtrusionShaders.fsSource}
        `

    const shaderProgram = Cesium.ShaderProgram.fromCache({
      context,
      vertexShaderSource,
      fragmentShaderSource,
      attributeLocations,
    })

    this.command = new Cesium.DrawCommand({
      vertexArray,
      shaderProgram,
      uniformMap,
      renderState,
      pass: translucent ? Cesium.Pass.TRANSLUCENT : Cesium.Pass.OPAQUE,
      modelMatrix: this.modelMatrix,
    })
  }

  update(frameState) {
    if (!frameState.passes.render || !this.show) return

    if (!this.command) {
      this.rebuild(frameState.context)
    }
    frameState.commandList.push(this.command)
  }

  destroy() {
    this.command?.shaderProgram.destroy()
    return Cesium.destroyObject(this)
  }
}

export default WeGridExtrusion
